package com.lantosec.hmrp.modules.system.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lantosec.hmrp.core.exception.AppException;
import com.lantosec.hmrp.core.kit.PageVO;
import com.lantosec.hmrp.core.util.DateUtils;
import com.lantosec.hmrp.core.util.ExcelUtils;
import com.lantosec.hmrp.modules.form.mapper.FormColumnsMapper;
import com.lantosec.hmrp.modules.system.entity.SysCustomReport;
import com.lantosec.hmrp.modules.system.enumeration.ReportConditionTypeEnum;
import com.lantosec.hmrp.modules.system.enumeration.ReportFieldDataTypeEnum;
import com.lantosec.hmrp.modules.system.mapper.SysCustomReportMapper;
import com.lantosec.hmrp.modules.system.service.ISysCustomReportService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lantosec.hmrp.modules.system.vo.ReportConditionVO;
import com.lantosec.hmrp.modules.system.vo.ReportExecutionVO;
import com.lantosec.hmrp.modules.system.vo.ReportFieldLayoutVO;
import com.lantosec.hmrp.modules.system.vo.SysUserVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Primary;
import lombok.extern.log4j.Log4j2;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 自定义报表查询表 服务实现类
 * </p>
 *
 * @author AutoCode
 * @since 2020-06-14
 */
@Service
@Primary
@Log4j2
public class SysCustomReportServiceImpl extends ServiceImpl<SysCustomReportMapper, SysCustomReport> implements ISysCustomReportService {

    @Autowired
    FormColumnsMapper formColumnsMapper;

    /**
     * 同一表单不能有相同的布局名称
     *
     * @param entity
     * @return
     */
    private boolean isExist(SysCustomReport entity) {
        Integer count = baseMapper.selectCount(new QueryWrapper<SysCustomReport>()
                .notIn(StringUtils.isNotBlank(entity.getId()), String.valueOf(SysCustomReport.ColName.id), entity.getId())
                .eq(String.valueOf(SysCustomReport.ColName.report_name), entity.getReportName()));
        return count > 0;
    }

    @Override
    public boolean save(SysCustomReport entity){
        if (isExist(entity)) {
            throw new AppException("报表名称已经存在");
        }
        return super.save(entity);
    }

    @Override
    public boolean updateById(SysCustomReport entity) {
        if (isExist(entity)) {
            throw new AppException("报表名称已经存在");
        }
        return super.updateById(entity);
    }

    /**
     * 执行报表查询
     */
    @Override
    public Page<Map<String, Object>> executionQuery(PageVO<Map<String,Object>> page, ReportExecutionVO executionVO) {
        // 根据Id查询报表信息
        SysCustomReport entity = baseMapper.selectById(executionVO.getReportId());
        String sql = generateSql(entity, executionVO);
        List<Map<String, Object>> result;
        // 执行SQL，返回结果
        if (entity.getPagination() > 0) {
            // 分页
            result = formColumnsMapper.executePageSql(page, sql);
        } else {
            result = formColumnsMapper.executeSelectSql(sql);
        }
        return page.setRecords(result);
    }

    private String generateSql(SysCustomReport entity, ReportExecutionVO executionVO){
        if (Objects.isNull(entity)) {
            throw new AppException("该报表不存在");
        }
        if (entity.getEnable() == 0) {
            throw new AppException("该报表未启用");
        }

        // 获取报表的数据源SQL
        String sourceSql = entity.getReportSource();

        // 查询需要显示的字段列
        String viewFields = getViewFields(entity.getReportLayout());
        // 条件查询
        String where = getCondition(executionVO.getCondition(), entity.getReportCondition());
        // 组装SQL
        return "SELECT " + viewFields + " FROM (" + sourceSql + ") AS temp_table " + where;
    }

    /**
     * 获取需要显示的字段，以逗号隔开，用于SQL查询
     * @param reportLayout  报表布局JSON字符串
     * @return
     */
    private String getViewFields(String reportLayout) {
        StringBuilder fields = new StringBuilder();
        List<ReportFieldLayoutVO> fieldLayoutVOList = JSON.parseArray(reportLayout, ReportFieldLayoutVO.class);
        if (CollectionUtils.isEmpty(fieldLayoutVOList)) {
            // 如果没有则查询全部字段
            fields.append(" * ");
            return fields.toString();
        }
        fieldLayoutVOList.forEach(item -> {
            fields.append("`").append(item.getFieldName()).append("`,");
        });
        fields.delete(fields.length() - 1, fields.length());
        return fields.toString();
    }

    /**
     * 获取查询条件
     * @param condition         条件参数
     * @param reportCondition   报表条件配置
     * @return
     */
    private String getCondition(String condition, String reportCondition) {
        if (StringUtils.isEmpty(condition)) {
            return "";
        }
        // 解析条件JSON字符串，转成JSON
        JSONObject conditionJson = JSONObject.parseObject(condition);
        if (Objects.isNull(conditionJson) || conditionJson.isEmpty()) {
            return "";
        }
        // 自定义报表的条件参数配置
        List<ReportConditionVO> conditionVOList = JSON.parseArray(reportCondition, ReportConditionVO.class);

        // 拼接where条件
        StringBuilder where = new StringBuilder();
        where.append(" WHERE ");
        for (String field : conditionJson.keySet()) {
            // 条件值
            Object fieldObject = conditionJson.get(field);
            // 获取条件字段的配置信息，同一字段名可能有多个条件配置
            List<ReportConditionVO> fieldList = conditionVOList.stream().filter(a -> a.getFieldName().equals(field)).collect(Collectors.toList());
            if (Objects.isNull(fieldObject) || CollectionUtils.isEmpty(fieldList)) {
                continue;
            }
            // 同字段名的条件参数如果有多个，以逗号隔开
            String[] fieldValues = fieldObject.toString().split(",");
            for (int i = 0; i < fieldList.size(); i++) {
                if (fieldValues.length < i) {
                    break;
                }
                ReportConditionVO item = fieldList.get(i);
                // 属性值
                String fieldValue = fieldValues[i];
                // 条件类型，like或者大于，小于
                ReportConditionTypeEnum conditionTypeEnum = Enum.valueOf(ReportConditionTypeEnum.class, item.getConditionType());
                if (ReportConditionTypeEnum.LIKE.equals(conditionTypeEnum)) {
                    fieldValue = " CONCAT('%','" + fieldValue + "','%') ";
                } else {
                    ReportFieldDataTypeEnum fieldDataTypeEnum = Enum.valueOf(ReportFieldDataTypeEnum.class, item.getDataType());
                    // 如果字段类型是字符串和时间，则加上单引号
                    if (ReportFieldDataTypeEnum.CHAR.equals(fieldDataTypeEnum) || ReportFieldDataTypeEnum.DATE.equals(fieldDataTypeEnum)) {
                        fieldValue = "'" + fieldValue + "'";
                    }
                }
                where.append("`").append(field).append("` ").append(conditionTypeEnum.getType()).append(fieldValue).append(" AND ");
            }
        }
        where.delete(where.length() - 4, where.length());

        return where.toString();
    }

    @Override
    public void exportExcel(ReportExecutionVO executionVO, HttpServletResponse response) {
        SysCustomReport entity = baseMapper.selectById(executionVO.getReportId());
        String sql = generateSql(entity, executionVO);
        List<Map<String, Object>> dataList = formColumnsMapper.executeSelectSql(sql);
        String title = entity.getReportName() + "-" + DateUtils.getNowDate();
        List<ReportFieldLayoutVO> fieldLayoutVOList = JSON.parseArray(entity.getReportLayout(), ReportFieldLayoutVO.class);
        if(CollectionUtils.isEmpty(fieldLayoutVOList)){
            throw new AppException("该报表未配置需要显示的字段");
        }
        ExcelUtils.exportExcel(title, fieldLayoutVOList, dataList, response);
    }
}
